package yuqueRequest

import (
	// "fmt"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"os"
	"runtime"
	"strings"
	"sync"

	. "yuqueDownloader/entity"
	"yuqueDownloader/util"
	"yuqueDownloader/yuqueHandler"
	// "github.com/dlclark/regexp2"
	// "github.com/gocolly/colly/v2"
)

type YuqueRequest struct {
	Param       YuqueParam
	Wg          sync.WaitGroup
	BaseDirPath string
}

type YuqueParam struct {
	Yuque_host    string
	Url           string
	Cookies       map[string]string
	Yuque_session string
	Yuque_ctoken  string
}

func init() {

	log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
	log.SetPrefix("")

}

func InitParam(host string, yuque_session string, yuque_ctoken string) *YuqueRequest {
	var param = YuqueParam{}
	param.Yuque_host = host
	param.Yuque_session = yuque_session
	param.Yuque_ctoken = yuque_ctoken

	param.Cookies = make(map[string]string)
	param.Cookies["_yuque_session"] = yuque_session
	param.Cookies["yuque_ctoken"] = yuque_ctoken
	// return param

	request := YuqueRequest{
		Param: param,
	}
	return &request
}

// 生成一个新的 param
func (this *YuqueRequest) getParam(urlStr string) *YuqueParam {

	// 生成一个新对象
	newParam := this.Param

	if idx := strings.Index(urlStr, newParam.Yuque_host); idx >= 0 {
		urlStr = urlStr[idx:]
	}

	newParam.Url = urlStr

	// log.Println("生成请求参数", newParam)
	return &newParam
}

// 发送 Get 请求，带上 Cookies
func (this *YuqueRequest) doGet(urlStr string, params *map[string]string) (*http.Response, error) {
	yp := this.getParam(urlStr)

	urlPath, err := url.JoinPath(yp.Yuque_host, yp.Url)
	if err != nil {
		log.Println("url JoinPath ERROR", err)
		return nil, err
	}

	if params != nil && len(*params) > 0 {
		if !strings.Contains(urlStr, "?") {
			urlPath += "?nomeans=1"
		}
		for key, val := range *params {
			urlPath += ("&" + url.QueryEscape(key) + "=" + url.QueryEscape(val))
		}
	}

	// 创建一个Cookie jar
	jar, _ := cookiejar.New(nil)

	// 创建一个带有Cookie jar的HTTP客户端
	client := &http.Client{
		Jar: jar,
	}

	// 创建一个HTTP请求
	req, _ := http.NewRequest("GET", urlPath, nil)

	for key, val := range this.Param.Cookies {
		// 设置请求的Cookie
		cookie := &http.Cookie{
			Name:  key,
			Value: val,
		}
		req.AddCookie(cookie)
	}

	log.Println("Get-URL:", urlPath)
	// 发起HTTP请求
	resp, err := client.Do(req)

	if err != nil {
		log.Panicln("发起 GET 请求报错,ERROR:", err)
	}

	return resp, err
}

// 获取 AppData
func (this *YuqueRequest) GetAppData(urlStr string) (result *AppDataStruct, appDataStr string) {
	resp, _ := this.doGet(urlStr, nil)

	// fmt.Println("Response Status:", resp.StatusCode)
	// 输出响应
	if resp.StatusCode == 200 {
		bytes, err := io.ReadAll(resp.Body)
		if err != nil {
			log.Println("read Body ERROR", err)
		}
		appDataStr = util.ParseAppData(string(bytes))
		if len(appDataStr) > 0 {
			// log.Println("解析AppData:", appDataStr)

			var appData AppDataStruct
			err2 := json.Unmarshal([]byte(appDataStr), &appData)
			if err2 != nil {
				log.Panicln("解析 AppData 报错:", err2)
			}
			log.Println("appData.Book.Toc>", appData.Book.Tocs)
			result = &appData
		}
	} else {
		log.Fatalln("request ========> ", resp)
	}

	return
}

// 处理 Toc
func (this *YuqueRequest) HandleToc(book BookStruct, toc TocStruct) {
	defer func() {
		recover()
		this.Wg.Done()
	}()

	this.Wg.Add(1)

	slug := toc.Url
	params := make(map[string]string, 1)
	params["book_id"] = fmt.Sprintf("%d", book.Id)
	resp, err := this.doGet(fmt.Sprintf("/api/docs/%s", slug), &params)
	if err != nil {
		log.Printf("HandleToc ERROR, bookName: %s, tocUrl: %s\n", book.Name, toc.Url)
		return
	}

	if resp.StatusCode == 200 {
		bytes, err := io.ReadAll(resp.Body)
		if err != nil {
			log.Println("read Body ERROR", err)
			runtime.Goexit()
		}
		var docData DocStruct
		err = json.Unmarshal(bytes, &docData)
		if err != nil {
			log.Println("docData Unmarshal ERROR:", err)
			runtime.Goexit()
		}

		// 持久化 doc json
		go util.WriteFile(string(bytes),
			"doc",
			fmt.Sprintf("%s%cjson", this.BaseDirPath, os.PathSeparator),
			fmt.Sprintf("%03d-%s.json", toc.Idx, slug), &this.Wg)

		// 处理 doc 的 html
		go yuqueHandler.HandleHtml(docData.Data.Content, this.BaseDirPath, &toc, &this.Wg)

	} else {
		log.Println("HandleToc Request Error", resp, toc.Idx)
		// 请求失败，直接生成 md 文件
		content := util.GenWellSigns(toc.Level+1) + " " + toc.Title + "\n"
		go util.WriteFile(content,
			"md",
			fmt.Sprintf("%s%cmd", this.BaseDirPath, os.PathSeparator),
			fmt.Sprintf("%03d-%s.md", toc.Idx, slug), &this.Wg)

	}

}
